package org.demis.dwarf.core.mapping;

import org.demis.dwarf.core.db.*;
import org.demis.dwarf.core.mapping.util.NameConverter;
import org.demis.dwarf.core.object.*;
import org.demis.dwarf.core.object.util.OneToOne;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;

public class DB2Object {

    private static final Logger logger = LoggerFactory.getLogger(DB2Object.class);

    public static List<Entity> convertSchema(Schema schema) {
        logger.info("start convert schema #" + schema);

        List<Entity> entities = new ArrayList<Entity>();
        // First step convert table and column
        for (Table table: schema.getTables()) {
            Entity entity = convertTable(table);
            entities.add(entity);
            convertColumns(table);
            if (table.getPrimaryKey() != null) {
                convertPrimaryKey(table.getPrimaryKey());
            }
        }

        for (Table table: schema.getTables()) {
            for (ForeignKey foreignKey: table.getExportedKeys()) {
                if (detectOneToOne(foreignKey)) {
                    convertOneToOne(foreignKey);
                }
                else if (detectManyToMany(foreignKey)) {
                    convertManyToMany(foreignKey);
                }
                convertOneToMany(foreignKey);
            }
            for (ForeignKey foreignKey: table.getImportedKeys()) {
                convertManyToOne(foreignKey);
            }
        }

        logger.info("end convert schema #" + schema);
        return entities;
    }

    public static ManyToMany convertManyToMany(ForeignKey foreignKey) {
        return null;
    }

    public static OneToOne convertOneToOne(ForeignKey foreignKey) {
        return null;
    }

    public static ManyToOne convertManyToOne(ForeignKey foreignKey) {
        ManyToOne manyToOne = new ManyToOne();
        // Table -> Entity
        manyToOne.setOwner(DB2ObjectMapping.getInstance().getEntity(foreignKey.getImportedTable()));
        manyToOne.setAssociatedEntity(DB2ObjectMapping.getInstance().getEntity(foreignKey.getExportedTable()));
        // Columns -> RawAttributes
        for (Column column: foreignKey.getImportedColumns()) {

            manyToOne.addAssociatedAttributes(DB2ObjectMapping.getInstance().getRawAttribute(column),
                    DB2ObjectMapping.getInstance().getRawAttribute(foreignKey.getExportedColumn(column)));
        }
        // TODO Cascade

        manyToOne.getOwner().addManyToOne(manyToOne);
        return manyToOne;
    }

    public static OneToMany convertOneToMany(ForeignKey foreignKey) {
        OneToMany oneToMany = new OneToMany();
        // Table -> Entity
        oneToMany.setOwner(DB2ObjectMapping.getInstance().getEntity(foreignKey.getImportedTable()));
        oneToMany.setAssociatedEntity(DB2ObjectMapping.getInstance().getEntity(foreignKey.getExportedTable()));
        // Columns -> RawAttributes
        for (Column column: foreignKey.getImportedColumns()) {

            oneToMany.addAssociatedAttributes(DB2ObjectMapping.getInstance().getRawAttribute(column),
                    DB2ObjectMapping.getInstance().getRawAttribute(foreignKey.getExportedColumn(column)));
        }
        // TODO Cascade

        oneToMany.getOwner().addOneToMany(oneToMany);
        return oneToMany;
    }

    public static boolean detectOneToOne(ForeignKey foreignKey) {
        return false;
    }

    public static boolean detectManyToMany(ForeignKey foreignKey) {
        return false;
    }

    public static Id convertPrimaryKey(PrimaryKey primaryKey) {
        Id id = new Id();

        String name = null;
        if (primaryKey.nbColumn() > 1) {
            name = NameConverter.convertDataBaseName(primaryKey.getTable().getName()) + "PrimaryKey";
        }
        else {
            name = NameConverter.convertDataBaseName(primaryKey.getColumnAs(1).getName(), false);
        }

        name = NameConverter.toLowerCaseFirst(name);

        for (Column column : primaryKey.getColumns()) {
            id.addRawAttributes(DB2ObjectMapping.getInstance().getRawAttribute(column));
        }
        id.setName(name);

        Entity entity = DB2ObjectMapping.getInstance().getEntity(primaryKey.getTable());
        entity.setId(id);
        id.setOwner(entity);

        if (logger.isInfoEnabled()) {
            logger.info("primaryKey #" + primaryKey + " converted to id #" + id);
        }

        return id;
    }

    public static void convertColumns(Table table) {
        for (Column column : table.getColumns()) {
            RawAttribute attribute = convertColumn(column);
            DB2ObjectMapping.getInstance().getEntity(table).addRawAttribute(attribute);
            if (logger.isInfoEnabled()) {
                logger.info("column #" + column + " converted to raw attribute #" + attribute);
            }
            DB2ObjectMapping.getInstance().addMapping(column, attribute);
        }

    }

    public static RawAttribute convertColumn(Column column) {
        RawAttribute rawAttribute = new RawAttribute();
        // Process property name
        String attributeName = NameConverter.convertDataBaseName(column.getName(), !column.isPrimaryKey());
        attributeName = attributeName.substring(0,1).toLowerCase() + attributeName.substring(1,attributeName.length());
        rawAttribute.setName(attributeName);
        // Process java type
        rawAttribute.setJavaType(SQLTypeToClassConverter.convert(column.getType()));

        return rawAttribute;
    }

    public static Entity convertTable(Table table) {
        Entity entity = new Entity();
        entity.setName(NameConverter.convertDataBaseName(table.getName()));
        DB2ObjectMapping.getInstance().addMapping(table, entity);

        logger.info("table #" + table + " converted to entity #" + entity);
        return entity;
    }

}
